home *** CD-ROM | disk | FTP | other *** search
/ Delphi 2 - Developers' Solutions / Delphi 2 Developers' Solutions.iso / dds / chap11 / howto05 / ccuucode.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-02-27  |  36.4 KB  |  1,044 lines

  1. unit CCUUCode;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  7.   Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, Menus, FileCtrl;
  8.  
  9. const UUDefaultSuffix = '.UUO'; { Use this if no valid output suffix }
  10.       UUDefaultOuputSuffix = '.UUE'; { Use this for all encodings }
  11.       UUCodingOffset = 32; { This is standard offset for UU Coding }
  12.       CDV_ENCODE = 1; { Data vector for encoding }
  13.       CDV_DECODE = 2; { Data vector for decoding }
  14.       CIV_FILE   = 1; { Input vector for file }
  15.       CIV_STREAM = 2; { Input vector for stream }
  16.       CIV_SLIST  = 3; { Input vector for string list }
  17.       COV_FILE   = 1; { Output vector for file }
  18.       COV_STREAM = 2; { Output vector for stream }
  19.       COV_SLIST  = 3; { Output vector for string list }
  20.       CMV_SINGLE = 0; { Multifile vector single file }
  21.       CMV_MULTI  = 1; { Multifile vector multiple files }
  22.       EC_NOBEGIN = -1; { Error code for no Begin found }
  23.       EC_EOF     = -2; { Error code for unexpected end of file }
  24.       EC_EMPTYDATALINE = -3; { Error code for empty line in data }
  25.       EC_UEODL = -4; { Error code for unexpected end of data line }
  26.       EC_INVALIDCHAR = -5; { Error code for invalid char in stream }
  27.       EC_OUTPUTFILEERROR = -6; { Error code for failure on opening output file }
  28.       EC_INPUTFILEERROR = -7; { Error code for failure on opening input file }
  29.  
  30. type
  31.   TUUErrorEvent = procedure( ErrorCode : Integer; ErrorMessage : String )
  32.    of object;
  33.   TUUUpdateEvent = procedure( BytesCompleted , TotalBytes : LongInt )
  34.    of object;
  35.   { This object handles decoding streams, files (multiples), and output }
  36.   { to streams or files.                                                }
  37.   TUUCodingObject = class( TWinControl )
  38.   private { hidden stuff }
  39.     FOnUUErrorOccurred : TUUErrorEvent;
  40.     FOnOutputStatus    : TUUUpdateEvent;
  41.   public { public stuff }
  42.     CurrentInputFileName    : String;
  43.     CurrentOutputFileName   : String;
  44.     TheMultipleFilesList    : TStringList;
  45.     TheEncodingOutputFile ,
  46.     TheInputFile            : TextFile;
  47.     TheEncodingInputFile ,
  48.     TheOutputFile           : File of Byte;
  49.     CurrentMFInPointer      : Integer;
  50.     CurrentLineNumber       : integer;
  51.     CurrentLine             : string;
  52.     CurrentErrorCode        : Integer;
  53.     CurrentErrorMessage     : String;
  54.     CurrentMultifileVector  : Integer;
  55.     constructor Create( AOwner : TComponent ); override;
  56.     destructor Destroy; override;
  57.     procedure UUError( ECode : Integer; EMsg : String );
  58.     procedure UUUpdate( BSF , BT : LongInt );
  59.     function GetTextFileSize( TheName : String ) : Longint;
  60.     function SetInputFileName( TheName : String ) : Boolean;
  61.     procedure SetMultipleFilesList( TheList : TStringList );
  62.     function DecodeOutputName( TheInputString : String ) : String;
  63.     procedure SetMultiFileVector( TheVector : Integer );
  64.     procedure GetNextInputFileLine( var OutputString : string );
  65.     procedure DecodeLine;
  66.     function StartDecoding : Boolean;
  67.     function Decode : Boolean;
  68.     function DecodeCurrentInputs : Boolean;
  69.     function EncodeCurrentInputs : Boolean;
  70.     procedure AbortCoding( AbortCode : Integer; AbortMessage : String );
  71.     procedure GetNextSDWord(     TheInputString : String;
  72.                              var WordGotten     : string;
  73.                              var PositionIndex  : integer );
  74.     function GetAUsableSingleExtensionFileName( InputName : String ) : String;
  75.     function ScanLinesforDecodeStartup : String;
  76.     function ScanLinesforBEGINEND( Vector : Integer ) : Boolean;
  77.     function CheckForBEGIN_ENDLine( InputLine : String; Vector : Integer ) : boolean;
  78.     function CheckForValidLine : boolean;
  79.  
  80.     property OnUUErrorOccurred : TUUErrorEvent read FOnUUErrorOccurred
  81.      write FOnUUErrorOccurred;
  82.     property OnOutputStatus : TUUUpdateEvent read FOnOutputStatus
  83.      write FOnOutputStatus;
  84.   end;
  85. var
  86.   TotalBytesSoFar ,
  87.   TotalBytesToDo    : Longint;
  88.  
  89. implementation
  90.  
  91. uses CCICCFRM;
  92.  
  93. { Create call }
  94. constructor TUUCodingObject.Create( AOwner : TComponent );
  95. begin
  96.   { Inherited create }
  97.   inherited Create( AOwner );
  98.   { set all internals to neutral }
  99.   CurrentMFInPointer      := 0;
  100.   CurrentLineNumber       := 0;
  101.   CurrentLine             := '';
  102.   CurrentErrorCode        := 0;
  103.   CurrentErrorMessage     := '';
  104.   CurrentMultifileVector  := CMV_SINGLE;
  105.   FOnUUErrorOccurred := UUError;
  106.   FOnOutputStatus    := UUUpdate;
  107.  
  108. end;
  109.  
  110. { Replacement destroy; currently does nada }
  111. destructor TUUCodingObject.Destroy;
  112. begin
  113.   { call inherited }
  114.   Inherited Destroy;
  115. end;
  116.  
  117. { This is the generic error handler }
  118. procedure TUUCodingObject.UUError( ECode : Integer; EMsg : String );
  119. begin
  120.   { Do generic MessageBox }
  121.   MessageDlg( 'A UUCode error code ' + IntToStr( ECode ) +
  122.    ' has happend with Message ' + EMsg , mtError , [mbOK] , 0 );
  123. end;
  124.  
  125. { This is the generic update procedure }
  126. procedure TUUCodingObject.UUUpdate( BSF , BT : LongInt );
  127. begin
  128.   CCInetCCForm.UpdateUUGauge( BSF , BT );
  129. end;
  130.  
  131. { This is a clever function to get the total bytes of a text file }
  132. function TUUCodingObject.GetTextFileSize( TheName : String ) : Longint;
  133. var TheSR : TSearchRec; { Used for trick }
  134. begin
  135.   { This allows getting the data }
  136.   FindFirst( TheName , faAnyFile , TheSR );
  137.   { And this is the info }
  138.   Result := TheSR.Size;
  139.   { Needed for win32 }
  140.   {FindClose( TheSR )};
  141. end;
  142.  
  143. { This method sets a filename for input of single file data }
  144. function TUUCodingObject.SetInputFileName( TheName : String ) : Boolean;
  145. begin
  146.   { Set the file var to imported name }
  147.   CurrentInputFileName := TheName;
  148.   Result := true;
  149. end;
  150.  
  151. { This method sets up an ordered list of files to send through decoding }
  152. procedure TUUCodingObject.SetMultipleFilesList( TheList : TStringList );
  153. begin
  154.   { Set the multiple files list to imported list }
  155.   TheMultipleFilesList := TheList;
  156. end;
  157.  
  158. { This method obtains the output file name if file-based output }
  159. { If not it still gets the output name and saves it.            }
  160. function TUUCodingObject.StartDecoding : Boolean;
  161. var HoldingString ,
  162.     TempName        : String;
  163.     Counter_1       : Integer;
  164.     Through         : Boolean;
  165. begin
  166.   Result := false;
  167.   case CurrentMultiFileVector of
  168.     CMV_SINGLE : begin { Single Input File }
  169.                    TotalBytesSoFar := 0;
  170.                    TotalBytesToDo := GetTextFileSize( CurrentInputFileName );
  171.                    try
  172.                      AssignFile( TheInputFile ,
  173.                                  CurrentInputFileName );
  174.                      Reset( TheInputFile );
  175.                      HoldingString :=
  176.                       ScanLinesForDecodeStartup;
  177.                      if HoldingString = '' then
  178.                      begin
  179.                        AbortCoding( EC_NOBEGIN ,
  180.                                     'No Begin Found!' );
  181.                        Result := false;
  182.                        exit;
  183.                      end
  184.                      else
  185.                      begin
  186.                        CurrentOutputFileName := NewsPath + '\' +
  187.                         HoldingString;
  188.                        try
  189.                          AssignFile( TheOutputFile ,
  190.                           CurrentOutputFileName );
  191.                          Rewrite( TheOutputFile );
  192.                          result := true;
  193.                        except
  194.                          On EInOutError do
  195.                          begin
  196.                            AbortCoding( EC_OUTPUTFILEERROR ,
  197.                             'Error Opening Output File ' );
  198.                            Result := false;
  199.                            exit;
  200.                          end;
  201.                        end;
  202.                      end;
  203.                    except
  204.                      On EInOutError do
  205.                      begin
  206.                        AbortCoding( EC_INPUTFILEERROR ,
  207.                         'Error Opening Input File ' );
  208.                        Result := false;
  209.                        exit;
  210.                      end;
  211.                    end;
  212.                  end;
  213.     CMV_MULTI  : begin { Multiple Input Files }
  214.                    Counter_1 := 0;
  215.                    Through := false;
  216.                    while not Through do
  217.                    begin
  218.                      if ( Counter_1 + 1 ) > TheMultipleFilesList.Count then
  219.                      begin
  220.                        AbortCoding( EC_NOBEGIN , 'No Begin Found!' );
  221.                        Result := false;
  222.                        exit;
  223.                      end;
  224.                      TempName := TheMultipleFilesList.Strings[ Counter_1 ];
  225.                      CurrentInputFileName := TempName;
  226.                      TotalBytesSoFar := 0;
  227.                      TotalBytesToDo := GetTextFileSize( CurrentInputFileName );
  228.                      AssignFile( TheInputFile ,
  229.                                  CurrentInputFileName );
  230.                      Reset( TheInputFile );
  231.                      HoldingString :=
  232.                       ScanLinesForDecodeStartup;
  233.                      if HoldingString <> '' then
  234.                      begin
  235.                        CurrentMFInPointer := Counter_1;
  236.                        CurrentOutputFileName :=
  237.                         HoldingString;
  238.                        try
  239.                          AssignFile( TheOutputFile ,
  240.                           CurrentOutputFileName );
  241.                          Rewrite( TheOutputFile );
  242.                          Through := true;
  243.                          Result := true;
  244.                        except
  245.                          On EInOutError do
  246.                          begin
  247.                            AbortCoding( EC_OUTPUTFILEERROR ,
  248.                             'Error Opening Output File ' );
  249.                            Result := false;
  250.                            exit;
  251.                          end;
  252.                        end;
  253.                      end
  254.                      else
  255.                      begin
  256.                        CloseFile( TheInputFile );
  257.                        Result := false;
  258.                      end;
  259.                    end;
  260.                  end;
  261.   end;
  262. end;
  263.  
  264. { This function attempts to decode one or more files and output the bytes }
  265. function TUUCodingObject.Decode : Boolean;
  266. var Through   : Boolean;
  267.     Finished  : Boolean;
  268.     TempName  : String;
  269. begin
  270.   Result := false;
  271.   case CurrentMultiFileVector of
  272.     CMV_SINGLE : begin
  273.                    If not StartDecoding then exit;
  274.                    if DecodeCurrentInputs then
  275.                    begin
  276.                      if Assigned( FOnOutputStatus ) then
  277.                       FOnOutputStatus( TotalBytesToDo , TotalBytesToDo );
  278.                      CloseFile( TheInputFile );
  279.                      CloseFile( TheOutputFile );
  280.                      Result := true;
  281.                      exit;
  282.                    end
  283.                    else
  284.                    begin
  285.                      Result := false;
  286.                      exit;
  287.                    end;
  288.                  end;
  289.     CMV_MULTI  : begin
  290.                    if not StartDecoding then exit;
  291.                    Through := false;
  292.                    while not Through do
  293.                    begin
  294.                      if not DecodeCurrentInputs then
  295.                      begin
  296.                        CloseFile( TheInputFile );
  297.                        CloseFile( TheOutputFile );
  298.                        Result := false;
  299.                        exit;
  300.                      end;
  301.                      if CurrentErrorCode = 2 then
  302.                      begin { Still getting data; keep looking }
  303.                        CurrentMFInPointer := CurrentMFInPointer + 1;
  304.                        if CurrentMFInPointer > TheMultipleFilesList.Count then
  305.                        begin
  306.                          Result := false;
  307.                          CloseFile( TheInputFile );
  308.                          CloseFile( TheOutputFile );
  309.                          exit;
  310.                        end
  311.                        else
  312.                        begin
  313.                          CloseFile( TheInputFile );
  314.                          TempName :=
  315.                           TheMultipleFilesList.Strings[ CurrentMFInPointer ];
  316.                          CurrentInputFileName := TempName;
  317.                         TotalBytesSoFar := 0;
  318.                         TotalBytesToDo := GetTextFileSize( CurrentInputFileName );
  319.                          AssignFile( TheInputFile ,
  320.                                      CurrentInputFileName );
  321.                          Reset( TheInputFile );
  322.                          CurrentLineNumber := 0;
  323.                          Finished := false;
  324.                          CurrentErrorCode := 0;
  325.                          while not Finished do
  326.                          begin
  327.                            GetNextInputFileLine( CurrentLine );
  328.                            if CheckForBEGIN_ENDLine( CurrentLine , 1 ) then
  329.                            begin
  330.                              Finished := true;
  331.                            end
  332.                            else
  333.                            begin
  334.                              if CurrentErrorCode <> 0 then
  335.                              begin
  336.                                AbortCoding( EC_NOBEGIN ,
  337.                                 'Multi-File File without BEGIN-' );
  338.                                Result := false;
  339.                                exit;
  340.                              end;
  341.                            end;
  342.                          end;
  343.                        end;
  344.                      end
  345.                      else
  346.                      begin
  347.                        Result := true;
  348.                        CloseFile( TheInputFile );
  349.                        CloseFile( TheOutputFile );
  350.                        Through := true;
  351.                      end;
  352.                    end;
  353.                  end;
  354.   end;
  355. end;
  356.  
  357. { This sets the multiple file vector }
  358. procedure TUUCodingObject.SetMultiFileVector( TheVector : Integer );
  359. begin
  360.   CurrentMultiFileVector := TheVector;
  361. end;
  362.  
  363. { This is the encoding method; it stuffs everything into one box for simplicity}
  364. function TUUCodingObject.EncodeCurrentInputs : Boolean;
  365. const EncodingOffset          = 32;
  366.       CharactersPerOutputLine = 60;
  367.       BytesPerDataGroup       = 3;
  368.       TotalLinesPerFile       = 900;
  369.       DataBitMask             = $3F;
  370. var EncodingLineLength             ,
  371.     NumberOfBytesProcessed         ,
  372.     CurrentTotalBytesInEncodedLine   : integer;
  373.     OutputLine                       : array [ 0 .. 59 ] of char;
  374.     DataGroup                        : array [ 0 .. 2 ] of byte;
  375.     CharactersToOutput               : array [ 0 .. 3 ] of byte;
  376.     TotalLinesOutputThisFile         : Integer;
  377.     CurrentOutputFileNumber          : Integer;
  378.   { This method writes a full line of output to the destination file }
  379.   procedure WriteOutputLineToFile;
  380.   var Counter_1: integer;
  381.     { This method writes a single character to the dest file, changing space to '}
  382.     procedure WriteSingleCharacterToFile( CurrentCharacter : char );
  383.     begin
  384.       if CurrentCharacter = ' ' then write( TheEncodingOutputFile , '`' )
  385.        else write( TheEncodingOutputFile , CurrentCharacter )
  386.     end;
  387.   begin {WriteOutputLineToFile}
  388.     try
  389.       WriteSingleCharacterToFile( Chr( CurrentTotalBytesInEncodedLine + 32 ));
  390.       for Counter_1 := 0 to EncodingLineLength - 1 do
  391.        WriteSingleCharacterToFile( OutputLine[ Counter_1 ]);
  392.       writeln ( TheEncodingOutputFile );
  393.       TotalLinesOutputThisFile := TotalLinesOutputThisFile + 1;
  394.       EncodingLineLength := 0;
  395.       CurrentTotalBytesInEncodedLine := 0;
  396.       if Assigned( FOnOutputStatus ) then
  397.        FOnOutputStatus( TotalBytesSoFar, TotalBytesToDo );
  398.     except
  399.       On EInOutError do
  400.       begin
  401.         AbortCoding( EC_OUTPUTFILEERROR , 'Unable to write file during encoding' );
  402.         exit;
  403.       end;
  404.     end;
  405.   end;
  406.   { This method sends encoded characters to the output line and sends a full line to file }
  407.   procedure WriteCharsToOutputLine;
  408.   var Counter_1: integer;
  409.   begin
  410.     if EncodingLineLength = 60 then WriteOutputLineToFile;
  411.     CharactersToOutput[ 0 ] := DataGroup[ 0 ] shr 2;
  412.     CharactersToOutput[ 1 ] := ( DataGroup[ 0 ] shl 4 ) + ( DataGroup[ 1 ] shr 4 );
  413.     CharactersToOutput[ 2 ] := ( DataGroup[ 1 ] shl 2 ) + ( DataGroup[ 2 ] shr 6 );
  414.     CharactersToOutput[ 3 ] := DataGroup[ 2 ] and DataBitMask;
  415.     for Counter_1 := 0 to 3 do
  416.     begin
  417.       OutputLine[ EncodingLineLength ] := Chr(( CharactersToOutput[ Counter_1 ]
  418.        and DataBitMask ) + EncodingOffset );
  419.       EncodingLineLength := EncodingLineLength + 1;
  420.     end;
  421.     NumberOfBytesProcessed := 0;
  422.     Inc(  CurrentTotalBytesInEncodedLine , 3 );
  423.   end;
  424.   { This procedure reads in one byte at a time of the input file; when a group }
  425.   { has been accumulated it flushes it into the data line which in turn sends  }
  426.   { it to the output line.                                                     }
  427.   procedure EncodeOneInputByte;
  428.   begin
  429.     if NumberOfBytesProcessed = 3 then WriteCharsToOutputLine;
  430.     try
  431.       seek( TheEncodingInputFile , TotalBytesSoFar );
  432.       read( TheEncodingInputFile , DataGroup[ NumberOfBytesProcessed ]);
  433.     except
  434.       On EInOutError do
  435.       begin
  436.         AbortCoding( EC_INPUTFILEERROR , 'Unable to read file for encoding' );
  437.         exit;
  438.       end;
  439.     end;
  440.     NumberOfBytesProcessed := NumberOfBytesProcessed + 1;
  441.     Inc( TotalBytesSoFar );
  442.   end; {EncodeOneInputByte}
  443.   { This procedure sends out valid final bytes }
  444.   procedure TerminateProperly;
  445.   begin
  446.     try
  447.       if NumberOfBytesProcessed > 0 then WriteCharsToOutputLine;
  448.       if EncodingLineLength > 0 then
  449.       begin
  450.         WriteOutputLineToFile;
  451.         WriteOutputLineToFile;
  452.       end
  453.       else WriteOutputLineToFile;
  454.       writeln( TheEncodingOutputFile , 'end' );
  455.       CloseFile( TheEncodingInputFile );
  456.       CloseFile( TheEncodingOutputFile );
  457.     except
  458.       On EInOutError do
  459.       begin
  460.         AbortCoding( EC_OUTPUTFILEERROR , 'Unable to close files during encoding' );
  461.         exit;
  462.       end;
  463.     end;
  464.   end;
  465.   { This sets up the input and output files }
  466.   procedure ProcessInitialFiles;
  467.   begin
  468.     try
  469.       AssignFile( TheEncodingInputFile , CurrentInputFileName );
  470.       Reset( TheEncodingInputFile );
  471.       AssignFile( TheEncodingOutputFile , ExpandFileName( 'CCOUT001.UUE' ));
  472.       Rewrite( TheEncodingOutputFile );
  473.       Writeln( TheEncodingOutputFile , 'Encoded by CC Internet Command Center V0.158' );
  474.       Writeln( TheEncodingOutputFile , 'File [' , IntToStr( CurrentOutputFileNumber )
  475.        , '] of File ' , LowerCase( ExtractFileName( CurrentInputFileName )));
  476.       Writeln( TheEncodingOutputFile , 'begin 666 ' ,
  477.        LowerCase( ExtractFileName( CurrentInputFileName )));
  478.     except
  479.       On EInOutError do
  480.       begin
  481.         AbortCoding( EC_INPUTFILEERROR , 'Unable to open files for encoding' );
  482.         exit;
  483.       end;
  484.     end;
  485.   end;
  486.   { This closes an output file and opens a new one, updating the name }
  487.   procedure ProcessNewEncodingOutputFile;
  488.   var TempName : String;
  489.   begin
  490.     try
  491.       Writeln( TheEncodingOutputFile , 'END-------CUT HERE----------' );
  492.       CloseFile( TheEncodingOutputFile );
  493.       TempName := IntToStr( CurrentOutputFileNumber + 1 );
  494.       while Length( TempName ) < 3 do TempName := '0' + TempName;
  495.       TempName := 'CCOUT' + TempName + '.UUE';
  496.       AssignFile( TheEncodingOutputFile , ExpandFileName( TempName ));
  497.       Rewrite( TheEncodingOutputFile );
  498.       Writeln( TheEncodingOutputFile , 'Encoded by CC Internet Command Center V0.158' );
  499.       Writeln( TheEncodingOutputFile , 'File [' , IntToStr( CurrentOutputFileNumber )
  500.        , '] of File ' , LowerCase( ExtractFileName( CurrentInputFileName )));
  501.       Writeln( TheEncodingOutputFile , 'BEGIN------CUT HERE----------' );
  502.       CurrentOutputFileNumber := CurrentOutputFileNumber + 1;
  503.       TotalLinesOutputThisFile := 0;
  504.     except
  505.       On EInOutError do
  506.       begin
  507.         AbortCoding( EC_OUTPUTFILEERROR , 'Unable to open files during encoding' );
  508.         exit;
  509.       end;
  510.     end;
  511.   end;
  512. { This method sets the control variable, reads in all data, and flushes the last buffer }
  513. begin
  514.   CurrentErrorCode := 0;
  515.   EncodingLineLength := 0;
  516.   TotalLinesOutputThisFile := 0;
  517.   CurrentOutputFileNumber := 1;
  518.   NumberOfBytesProcessed := 0;
  519.   CurrentTotalBytesInEncodedLine := 0;
  520.   ProcessInitialFiles;
  521.   if CurrentErrorCode <> 0 then
  522.   begin
  523.     Result := false;
  524.     exit;
  525.   end;
  526.   TotalBytesSoFar := 0;
  527.   TotalBytesToDo := Filesize( TheEncodingInputFile );
  528.   while not eof( TheEncodingInputFile ) do
  529.   begin
  530.     EncodeOneInputByte;
  531.     if CurrentErrorCode <> 0 then
  532.     begin
  533.       Result := false;
  534.       exit;
  535.     end;
  536.     if TotalLinesOutputThisFile > TotalLinesPerFile then
  537.     begin
  538.       ProcessNewEncodingOutputFile;
  539.       if CurrentErrorCode <> 0 then
  540.       begin
  541.         Result := false;
  542.         exit;
  543.       end;
  544.     end;
  545.   end;
  546.   TerminateProperly;
  547.   if CurrentErrorCode <> 0 then
  548.   begin
  549.     Result := false;
  550.     exit;
  551.   end;
  552.   Result := true;
  553. end;
  554.  
  555. { This procedure aborts decoding and shuts down the processing }
  556. procedure TUUCodingObject.AbortCoding( AbortCode : Integer; AbortMessage : string);
  557. begin
  558.   { Save abort code }
  559.   CurrentErrorCode := AbortCode;
  560.   { Save error message }
  561.   CurrentErrorMessage := AbortMessage;
  562.   { If error vector set send data to it }
  563.   if Assigned( FOnUUErrorOccurred ) then
  564.    FOnUUErrorOccurred(  CurrentErrorCode , CurrentErrorMessage );
  565.   { shut down input vector }
  566.   CloseFile( TheInputFile );
  567.   { shut down output vector }
  568.   CloseFile( TheOutputFile );
  569. end;
  570.  
  571. { Read a line of the Input file }
  572. procedure TUUCodingObject.GetNextInputFileLine( var OutputString : string );
  573. begin
  574.   CurrentLineNumber := CurrentLineNumber + 1;
  575.   try
  576.     Readln( TheInputFile , OutputString );
  577.     TotalBytesSoFar := TotalBytesSoFar + Length( OutputString );
  578.     if Assigned( FOnOutputStatus ) then
  579.      FOnOutputStatus( TotalBytesSoFar, TotalBytesToDo );
  580.   except
  581.     OutputString := '';
  582.     AbortCoding( EC_EOF , 'Unexpected End of File' );
  583.   end;
  584. end;
  585.  
  586. { This procedure obtains a space-delimited word from a string }
  587. procedure TUUCodingObject.GetNextSDWord(     TheInputString : String;
  588.                          var WordGotten     : string;
  589.                          var PositionIndex  : integer );
  590. begin
  591.   { Clear output word }
  592.   WordGotten := '';
  593.   { Run along until not at a space }
  594.   while TheInputString[ PositionIndex ] = ' ' do
  595.   begin
  596.     { Increment position index }
  597.     PositionIndex := PositionIndex + 1;
  598.     { if overrun string set error and abort }
  599.     if PositionIndex > length( TheInputString ) then
  600.     begin
  601.       WordGotten := '';
  602.       exit;
  603.     end;
  604.   end;
  605.   { Now run until find a space }
  606.   while TheInputString[ PositionIndex ] <> ' ' do
  607.   begin
  608.     { Add char to the word to get }
  609.     WordGotten := WordGotten + TheInputString[ PositionIndex ];
  610.     { move pointer up }
  611.     PositionIndex := PositionIndex + 1;
  612.     { abort silently if end of line }
  613.     if PositionIndex > length( TheInputString ) then
  614.     begin
  615.       exit;
  616.     end;
  617.   end
  618. end;
  619.  
  620. { This takes care of multiple dot UNIX filenames and fn > 12 or 8.3 }
  621. function TUUCodingObject.
  622.  GetAUsableSingleExtensionFileName( InputName : String ) : String;
  623. var HoldingString ,            { Strings to hold data while working }
  624.     TempString      : String;  { more so.                           }
  625.     BestPosition    : Integer; { Holds last period position for ext }
  626.     Counter_1       : Integer; { Loop counter                       }
  627. begin
  628.   { Set no dots found }
  629.   BestPosition := -1;
  630.   { Run loop to find last dot which marks extension }
  631.   for Counter_1 := 1 to Length( InputName ) do
  632.   begin
  633.     { Move counter to last position }
  634.     if InputName[ Counter_1 ] = '.' then BestPosition := Counter_1;
  635.   end;
  636.   { If not found to have an extension }
  637.   if BestPosition = -1 then
  638.   begin
  639.     { Grab first 8 chars, tack on default and exit }
  640.     HoldingString := Copy( InputName , 1 , 8 ) + UUDefaultSuffix;
  641.     Result := HoldingString;
  642.   end
  643.   else
  644.   begin
  645.     { If dotted filename }
  646.     if BestPosition = 1 then
  647.     begin
  648.       { Grab next 8 chars and put on default extension and exit }
  649.       HoldingString := Copy( InputName , 2 , 8 ) + UUDefaultSuffix;
  650.       Result := HoldingString;
  651.     end
  652.     else
  653.     begin
  654.       { copy to working string }
  655.       HoldingString := InputName;
  656.       { Convert all . but last one to _ }
  657.       For Counter_1 := 1 to BestPosition - 1 do
  658.       begin
  659.         { do the conversion }
  660.         if HoldingString[ Counter_1 ] = '.' then
  661.          HoldingString[ Counter_1 ] := '_';
  662.       end;
  663.       { if main name longer than 8 chars set it to that }
  664.       if BestPosition > 9 then
  665.       begin
  666.         { preserve original extension }
  667.         TempString := Copy( HoldingString , BestPosition , 255 );
  668.         HoldingString := Copy( HoldingString , 1 , 8 ) + TempString;
  669.       end;
  670.       { if remaining string longer than 8.3 then has oversize ext }
  671.       if Length( HoldingString ) > 12 then
  672.       begin
  673.         { So trim off all but first 12 chars }
  674.         HoldingString := Copy( HoldingString , 1 , 12 );
  675.       end;
  676.       { and return a result }
  677.       Result := HoldingString;
  678.     end;
  679.   end;
  680. end;
  681.  
  682. { This function checks for multipart block headers on lines }
  683. function TUUCodingObject.CheckForBEGIN_ENDLine( InputLine : String;
  684.                                                 Vector : Integer ) : boolean;
  685. begin
  686.   Result := false;
  687.   case Vector of
  688.     { BEGIN check }
  689.     1 : begin
  690.           { Do an uppercase; assume standard UU begin-space }
  691.           if Pos( 'BEGIN-' , Uppercase( InputLine )) = 1 then
  692.           begin
  693.             { If find hypenated begin assume cutline }
  694.             Result := true;
  695.           end
  696.           else
  697.           begin
  698.             { Otherwise keep scanning }
  699.             Result := false;
  700.           end;
  701.         end;
  702.     { END check }
  703.     2 : begin
  704.           { Do an uppercase; assume standard UU end only }
  705.           if Pos( 'END-' , Uppercase( InputLine )) = 1 then
  706.           begin
  707.             { If find hyphenated end assume cutline }
  708.             Result := true;
  709.           end
  710.           else
  711.           begin
  712.             if InputLine = '.' then
  713.             begin
  714.               Result := true;
  715.               exit;
  716.             end;
  717.             { Otherwise keep scanning }
  718.             Result := false;
  719.           end;
  720.         end;
  721.   end;
  722. end;
  723.  
  724. { This function returns true or false depending on getting output name }
  725. function TUUCodingObject.DecodeOutputName( TheInputString : String ) : String;
  726. var TheIndex     : Integer; { Index counter for double get }
  727.     ResultString : String;  { final result holder          }
  728. begin
  729.   { Check for begin space startup }
  730.   TheIndex := Pos( 'BEGIN ' , Uppercase( TheInputString ));
  731.   { If not found then set to null and exit; }
  732.   if TheIndex <> 1 then
  733.   begin
  734.     Result := '';
  735.     exit;
  736.   end;
  737.   { Set to start of mode integer }
  738.   TheIndex := 7;
  739.   { Clear return var }
  740.   ResultString := '';
  741.   { Get a mode integer }
  742.   GetNextSDWord( TheInputString , ResultString , TheIndex );
  743.   { throw it away }
  744.   ResultString := '';
  745.   { Get a filename }
  746.   GetNextSDWord( TheInputstring , ResultString , TheIndex );
  747.   if ResultString = '' then Result := '' else
  748.    { Return it through filename filter }
  749.    Result := GetAUsableSingleExtensionFileName( ResultString );
  750. end;
  751.  
  752. { This method scans for the line containing the filename in Decode }
  753. function TUUCodingObject.ScanLinesforDecodeStartup : String;
  754. var TestLine   ,           { Hold result of line get }
  755.     HoldResult   : String; { Hold result of decode   }
  756.     Through      : Boolean;
  757. begin
  758.   { Set flag }
  759.   Through := false;
  760.   { Run loop }
  761.   while not Through do
  762.   begin
  763.     { Get an input line }
  764.     GetNextInputFileLine( TestLine );
  765.     { If null then hit EOF prematurely; exit }
  766.     if EOF( TheInputFile ) then
  767.     begin
  768.       Result := '';
  769.       exit;
  770.     end;
  771.     { Scan for some kind of file name on line }
  772.     HoldResult := DecodeOutputName( TestLine );
  773.     { If no good then will be ''; otherwise got valid }
  774.     if HoldResult <> '' then
  775.     begin
  776.       { Return the result, set flag and exit }
  777.       Result := HoldResult;
  778.       exit;
  779.     end;
  780.   end;
  781. end;
  782.  
  783. { This method scans for the line containing BEGIN- or END- markers }
  784. function TUUCodingObject.ScanLinesforBEGINEND( Vector : Integer ) : Boolean;
  785. var HoldResult   : Boolean; { Hold result of decode   }
  786.     Through      : Boolean;
  787. begin
  788.   Result := false;
  789.   { Set flag }
  790.   Through := false;
  791.   { Run loop }
  792.   while not Through do
  793.   begin
  794.     { Get an input line }
  795.     GetNextInputFileLine( CurrentLine );
  796.     { If null then hit EOF prematurely; exit }
  797.     if CurrentLine = '' then
  798.     begin
  799.       case Vector of
  800.         1 : begin { BEGIN- search }
  801.               Result := false;
  802.               CurrentErrorCode := 1; { File has no data }
  803.               exit;
  804.             end;
  805.         2 : begin { END- search }
  806.               Result := false;
  807.               CurrentErrorCode := 2; { data ended withou END- }
  808.               exit;
  809.             end;
  810.       end;
  811.     end;
  812.     { Scan for some kind of file name on line }
  813.     HoldResult := CheckForBEGIN_ENDLine( CurrentLine , Vector );
  814.     case Vector of
  815.       1 : begin { BEGIN- search }
  816.             if HoldResult then
  817.             begin { BEGIN- found; data will follow }
  818.               Result := true;
  819.               CurrentErrorCode := 0;
  820.               exit;
  821.             end
  822.             else
  823.             begin
  824.               { Keep looking until found or run out of data }
  825.             end;
  826.           end;
  827.       2 : begin { END- search }
  828.             if HoldResult then
  829.             begin  { END- found; need to switch to next file }
  830.               Result := true;
  831.               CurrentErrorCode := 0;
  832.               exit;
  833.             end
  834.             else
  835.             begin  { END- not found; assume data still flowing }
  836.               Result := false;
  837.               CurrentErrorCode := 0;
  838.               exit;
  839.             end;
  840.           end;
  841.     end;
  842.   end;
  843. end;
  844.  
  845. { This functin makes sure an input line is not empty or the end symbol }
  846. function TUUCodingObject.CheckForValidLine : boolean;
  847. begin
  848.  { If empty line then signal error and abort }
  849.  if CurrentLine = '' then
  850.  begin
  851.    { Signal abort code and exit }
  852.    AbortCoding( EC_EMPTYDATALINE , 'Empty line in data' );
  853.    Result := false;
  854.    exit;
  855.  end;
  856.  { otherwise check for a space or pseudo-space indicating a 0 line }
  857.  CheckForValidLine := not ( CurrentLine[ 1 ] in [ ' ' , '`' ])
  858. end;
  859.  
  860. { Decode a complete line of input text }
  861. procedure TUUCodingObject.DecodeLine;
  862. var LineIndex          ,
  863.     CurrentByteNumber  ,
  864.     ByteCount          ,
  865.     Counter_1            : integer;
  866.     CharactersToDecode   : array [ 0 .. 3 ] of byte;
  867.     BinaryDataToOutput   : array [ 0 .. 2 ] of byte;
  868.  
  869.   { This internal function gets the next character in the input line }
  870.   function GetNextCharacter : Char;
  871.   begin
  872.     { Increment current character pointer }
  873.     LineIndex := LineIndex + 1;
  874.     { if overrun line then signal error and abort }
  875.     if LineIndex > Length( CurrentLine ) then
  876.     begin
  877.       AbortCoding( EC_UEODL , 'Unexpected End of Character Data in Line');
  878.       Result := Chr( 0 );
  879.       exit;
  880.     end;
  881.     { If hit invalid character then signal error and abort }
  882.     if not ( CurrentLine[ LineIndex ] in [ ' ' .. '`' ]) then
  883.     begin
  884.       AbortCoding( EC_INVALIDCHAR , 'Invalid Character in Data Line');
  885.       Result := Chr( 0 );
  886.       exit;
  887.     end;
  888.     { Do conversion on ' to space and return valid character }
  889.     if CurrentLine[LineIndex] = '`' then
  890.      GetNextCharacter := ' ' else
  891.       GetNextCharacter := CurrentLine[ LineIndex ]
  892.   end;
  893.  
  894.   { This is an internal procedure to write out a single byte of decoded data }
  895.   procedure DecodeByte;
  896.  
  897.     { This is an internal procedure to do the decoding and get new data when out }
  898.     procedure GetNextDataGroup;
  899.     var Counter_1 : integer; { Loop Counter }
  900.         Value1    : integer;
  901.     begin
  902.       { Read in }
  903.       for Counter_1 := 0 to 3 do
  904.       begin
  905.         Value1 := Ord( GetNextCharacter ) - UUCodingOffset;
  906.         if Value1 < 0 then exit;
  907.         CharactersToDecode[ Counter_1 ] := Value1;
  908.       end;
  909.       { Do binary bit shifts and additions to create real binary data }
  910.       BinaryDataToOutput[ 0 ] := ( CharactersToDecode[ 0 ] shl 2 ) +
  911.         ( CharactersToDecode[ 1 ] shr 4 );
  912.       BinaryDataToOutput[ 1 ] := ( CharactersToDecode[ 1 ] shl 4 ) +
  913.         ( CharactersToDecode[ 2 ] shr 2 );
  914.       BinaryDataToOutput[ 2 ] := ( CharactersToDecode[ 2 ] shl 6 ) +
  915.         CharactersToDecode[ 3 ];
  916.       CurrentByteNumber := 0;
  917.     end;
  918.  
  919.   { Begin DecodeByte procedure }
  920.   begin
  921.     { Clear error flag }
  922.     CurrentErrorCode := 0;
  923.     { If at end of current data get next group }
  924.     if CurrentByteNumber = 3 then GetNextDataGroup;
  925.     { If any error occurs exit at once }
  926.     if CurrentErrorCode <> 0 then exit;
  927.     { Write output bytes }
  928.     Write( TheOutputFile , BinaryDataToOutput[ CurrentByteNumber ]);
  929.     { Increment current byte number (note that it resets to 0 so won't overrun end}
  930.     CurrentByteNumber := CurrentByteNumber + 1;
  931.   end;
  932.  
  933. { Begin decode line procedure }
  934. begin
  935.   { Set start of data to 0; will be pre-incremented }
  936.   LineIndex := 0;
  937.   { Signal need for new data }
  938.   CurrentByteNumber := 3;
  939.   { Determine how many bytes on current line by  }
  940.   { Getting first character's ordinal value - 32 }
  941.   ByteCount := ( Ord( GetNextCharacter ) - UUCodingOffset );
  942.   { Run that many characters through the decode byte procedure }
  943.   { Which writes out bytes to output streams and gets new data }
  944.   { every three bytes. If less than 3 output bytes in last set }
  945.   { padding will be ignored.                                   }
  946.   for Counter_1 := 1 to ByteCount do DecodeByte
  947. end;
  948.  
  949. { This is the core decoding procedure for a current input stream }
  950. function TUUCodingObject.DecodeCurrentInputs : Boolean;
  951.  
  952.   { This is an internal function to get an input line }
  953.   function GetAnInputLine : Boolean;
  954.   begin
  955.     Result := true;
  956.     case CurrentMultiFileVector of
  957.       0 : begin { Single file decode; no END- issues }
  958.             CurrentErrorCode := 0;
  959.             GetNextInputFileline( CurrentLine );
  960.             if CurrentErrorCode <> 0 then exit;
  961.             result := true;
  962.             exit;
  963.           end;
  964.       1 : begin { Multiple file decode; must check for END- }
  965.             if ScanLinesForBEGINEND( 2 ) then
  966.             begin { END found; exit }
  967.               CurrentErrorCode := 2;
  968.               Result := true;
  969.               exit;
  970.             end
  971.             else
  972.             begin { END not found; check for end of file }
  973.               if CurrentErrorCode = 2 then
  974.               begin { Premature EOF; accept in multifile OK }
  975.                 Result := true;
  976.                 exit;
  977.               end
  978.               else
  979.               begin  { Either fatal error or OK line }
  980.                 if CurrentErrorCode < 0 then
  981.                 begin { Fatal error; abort }
  982.                   Result := false;
  983.                   exit;
  984.                 end
  985.                 else
  986.                 begin
  987.                   Result := true;
  988.                   exit;
  989.                 end;
  990.               end;
  991.             end;
  992.           end;
  993.     end;
  994.   end;
  995.  
  996. { Begin DecodeCurrentInputs function }
  997. begin
  998.   { If can't get valid input line then exit; }
  999.   if not GetAnInputLine then
  1000.   begin
  1001.     Result := false;
  1002.     exit;
  1003.   end;
  1004.   { If hit end of data in multiline environment then exit OK }
  1005.   if (( CurrentMultiFileVector = CMV_MULTI ) and ( CurrentErrorCode = 2 )) then
  1006.   begin
  1007.     Result := true;
  1008.     exit;
  1009.   end;
  1010.   { If hit end of data in single file environment signal error }
  1011.   if CurrentErrorCode = 2 then
  1012.   begin
  1013.     Result := false;
  1014.     exit;
  1015.   end;
  1016.   Result := true;
  1017.   { Run a check for a non-zero line; when hit zero line exit OK }
  1018.   while CheckForValidLine do
  1019.   begin
  1020.     { Decode entire line to appropriate output vector }
  1021.     DecodeLine;
  1022.     { If can't get valid input line then exit; }
  1023.     if not GetAnInputLine then
  1024.     begin
  1025.       Result := false;
  1026.       exit;
  1027.     end;
  1028.     { If hit end of data in multiline environment then exit OK }
  1029.     if (( CurrentMultiFileVector = CMV_MULTI ) and ( CurrentErrorCode = 2 )) then
  1030.     begin
  1031.       Result := true;
  1032.       exit;
  1033.     end;
  1034.     { If hit end of data in single file environment signal error }
  1035.     if CurrentErrorCode = 2 then
  1036.     begin
  1037.       Result := false;
  1038.       exit;
  1039.     end;
  1040.   end;
  1041. end;
  1042.  
  1043. end.
  1044.